/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"vm/ObjectGroup.h"#include"jsexn.h"#include"jshashutil.h"#include"jsobj.h"#include"builtin/DataViewObject.h"#include"gc/Marking.h"#include"gc/Policy.h"#include"gc/StoreBuffer.h"#include"gc/Zone.h"#include"js/CharacterEncoding.h"#include"vm/ArrayObject.h"#include"vm/RegExpObject.h"#include"vm/Shape.h"#include"vm/TaggedProto.h"#include"vm/UnboxedObject.h"#include"jsobjinlines.h"#include"vm/UnboxedObject-inl.h"usingnamespacejs;usingmozilla::DebugOnly;usingmozilla::PodZero;/////////////////////////////////////////////////////////////////////// ObjectGroup/////////////////////////////////////////////////////////////////////ObjectGroup::ObjectGroup(constClass*clasp,TaggedProtoproto,JSCompartment*comp,ObjectGroupFlagsinitialFlags){PodZero(this);/* Windows may not appear on prototype chains. */MOZ_ASSERT_IF(proto.isObject(),!IsWindow(proto.toObject()));MOZ_ASSERT(JS::StringIsASCII(clasp->name));this->clasp_=clasp;this->proto_=proto;this->compartment_=comp;this->flags_=initialFlags;setGeneration(zone()->types.generation);}voidObjectGroup::finalize(FreeOp*fop){if(newScriptDontCheckGeneration())newScriptDontCheckGeneration()->clear();fop->delete_(newScriptDontCheckGeneration());fop->delete_(maybeUnboxedLayoutDontCheckGeneration());if(maybePreliminaryObjectsDontCheckGeneration())maybePreliminaryObjectsDontCheckGeneration()->clear();fop->delete_(maybePreliminaryObjectsDontCheckGeneration());}voidObjectGroup::setProtoUnchecked(TaggedProtoproto){proto_=proto;MOZ_ASSERT_IF(proto_.isObject()&&proto_.toObject()->isNative(),proto_.toObject()->isDelegate());}voidObjectGroup::setProto(TaggedProtoproto){MOZ_ASSERT(singleton());setProtoUnchecked(proto);}size_tObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOfmallocSizeOf)const{size_tn=0;if(TypeNewScript*newScript=newScriptDontCheckGeneration())n+=newScript->sizeOfIncludingThis(mallocSizeOf);if(UnboxedLayout*layout=maybeUnboxedLayoutDontCheckGeneration())n+=layout->sizeOfIncludingThis(mallocSizeOf);returnn;}voidObjectGroup::setAddendum(AddendumKindkind,void*addendum,boolwriteBarrier/* = true */){MOZ_ASSERT(!needsSweep());MOZ_ASSERT(kind<=(OBJECT_FLAG_ADDENDUM_MASK>>OBJECT_FLAG_ADDENDUM_SHIFT));if(writeBarrier){// Manually trigger barriers if we are clearing new script or// preliminary object information. Other addendums are immutable.switch(addendumKind()){caseAddendum_PreliminaryObjects:PreliminaryObjectArrayWithTemplate::writeBarrierPre(maybePreliminaryObjects());break;caseAddendum_NewScript:TypeNewScript::writeBarrierPre(newScript());break;caseAddendum_None:break;default:MOZ_ASSERT(addendumKind()==kind);}}flags_&=~OBJECT_FLAG_ADDENDUM_MASK;flags_|=kind<<OBJECT_FLAG_ADDENDUM_SHIFT;addendum_=addendum;}/* static */boolObjectGroup::useSingletonForClone(JSFunction*fun){if(!fun->isInterpreted())returnfalse;if(fun->isArrow())returnfalse;if(fun->isSingleton())returnfalse;/* * When a function is being used as a wrapper for another function, it * improves precision greatly to distinguish between different instances of * the wrapper; otherwise we will conflate much of the information about * the wrapped functions. * * An important example is the Class.create function at the core of the * Prototype.js library, which looks like: * * var Class = { * create: function() { * return function() { * this.initialize.apply(this, arguments); * } * } * }; * * Each instance of the innermost function will have a different wrapped * initialize method. We capture this, along with similar cases, by looking * for short scripts which use both .apply and arguments. For such scripts, * whenever creating a new instance of the function we both give that * instance a singleton type and clone the underlying script. */uint32_tbegin,end;if(fun->hasScript()){if(!fun->nonLazyScript()->isLikelyConstructorWrapper())returnfalse;begin=fun->nonLazyScript()->sourceStart();end=fun->nonLazyScript()->sourceEnd();}else{if(!fun->lazyScript()->isLikelyConstructorWrapper())returnfalse;begin=fun->lazyScript()->begin();end=fun->lazyScript()->end();}returnend-begin<=100;}/* static */boolObjectGroup::useSingletonForNewObject(JSContext*cx,JSScript*script,jsbytecode*pc){/* * Make a heuristic guess at a use of JSOP_NEW that the constructed object * should have a fresh group. We do this when the NEW is immediately * followed by a simple assignment to an object's .prototype field. * This is designed to catch common patterns for subclassing in JS: * * function Super() { ... } * function Sub1() { ... } * function Sub2() { ... } * * Sub1.prototype = new Super(); * Sub2.prototype = new Super(); * * Using distinct groups for the particular prototypes of Sub1 and * Sub2 lets us continue to distinguish the two subclasses and any extra * properties added to those prototype objects. */if(script->isStarGenerator()||script->isLegacyGenerator()||script->isAsync())returnfalse;if(JSOp(*pc)!=JSOP_NEW)returnfalse;pc+=JSOP_NEW_LENGTH;if(JSOp(*pc)==JSOP_SETPROP){if(script->getName(pc)==cx->names().prototype)returntrue;}returnfalse;}/* static */boolObjectGroup::useSingletonForAllocationSite(JSScript*script,jsbytecode*pc,JSProtoKeykey){// The return value of this method can either be tested like a boolean or// passed to a NewObject method.JS_STATIC_ASSERT(GenericObject==0);/* * Objects created outside loops in global and eval scripts should have * singleton types. For now this is only done for plain objects, but not * typed arrays or normal arrays. */if(script->functionNonDelazifying()&&!script->treatAsRunOnce())returnGenericObject;if(key!=JSProto_Object)returnGenericObject;// All loops in the script will have a try note indicating their boundary.if(!script->hasTrynotes())returnSingletonObject;unsignedoffset=script->pcToOffset(pc);JSTryNote*tn=script->trynotes()->vector;JSTryNote*tnlimit=tn+script->trynotes()->length;for(;tn<tnlimit;tn++){if(tn->kind!=JSTRY_FOR_IN&&tn->kind!=JSTRY_FOR_OF&&tn->kind!=JSTRY_LOOP)continue;unsignedstartOffset=script->mainOffset()+tn->start;unsignedendOffset=startOffset+tn->length;if(offset>=startOffset&&offset<endOffset)returnGenericObject;}returnSingletonObject;}/* static */boolObjectGroup::useSingletonForAllocationSite(JSScript*script,jsbytecode*pc,constClass*clasp){returnuseSingletonForAllocationSite(script,pc,JSCLASS_CACHED_PROTO_KEY(clasp));}/////////////////////////////////////////////////////////////////////// JSObject/////////////////////////////////////////////////////////////////////boolJSObject::shouldSplicePrototype(){/* * During bootstrapping, if inference is enabled we need to make sure not * to splice a new prototype in for Function.prototype or the global * object if their __proto__ had previously been set to null, as this * will change the prototype for all other objects with the same type. */if(staticPrototype()!=nullptr)returnfalse;returnisSingleton();}/* static */boolJSObject::splicePrototype(JSContext*cx,HandleObjectobj,constClass*clasp,Handle<TaggedProto>proto){MOZ_ASSERT(cx->compartment()==obj->compartment());/* * For singleton groups representing only a single JSObject, the proto * can be rearranged as needed without destroying type information for * the old or new types. */MOZ_ASSERT(obj->isSingleton());// Windows may not appear on prototype chains.MOZ_ASSERT_IF(proto.isObject(),!IsWindow(proto.toObject()));if(proto.isObject()){RootedObjectprotoObj(cx,proto.toObject());if(!JSObject::setDelegate(cx,protoObj))returnfalse;}// Force type instantiation when splicing lazy group.RootedObjectGroupgroup(cx,JSObject::getGroup(cx,obj));if(!group)returnfalse;RootedObjectGroupprotoGroup(cx,nullptr);if(proto.isObject()){RootedObjectprotoObj(cx,proto.toObject());protoGroup=JSObject::getGroup(cx,protoObj);if(!protoGroup)returnfalse;}group->setClasp(clasp);group->setProto(proto);returntrue;}/* static */ObjectGroup*JSObject::makeLazyGroup(JSContext*cx,HandleObjectobj){MOZ_ASSERT(obj->hasLazyGroup());MOZ_ASSERT(cx->compartment()==obj->compartment());// Find flags which need to be specified immediately on the object.// Don't track whether singletons are packed.ObjectGroupFlagsinitialFlags=OBJECT_FLAG_SINGLETON|OBJECT_FLAG_NON_PACKED;if(obj->isIteratedSingleton())initialFlags|=OBJECT_FLAG_ITERATED;if(obj->isIndexed())initialFlags|=OBJECT_FLAG_SPARSE_INDEXES;if(obj->is<ArrayObject>()&&obj->as<ArrayObject>().length()>INT32_MAX)initialFlags|=OBJECT_FLAG_LENGTH_OVERFLOW;Rooted<TaggedProto>proto(cx,obj->taggedProto());ObjectGroup*group=ObjectGroupCompartment::makeGroup(cx,obj->getClass(),proto,initialFlags);if(!group)returnnullptr;AutoEnterAnalysisenter(cx);/* Fill in the type according to the state of this object. */if(obj->is<JSFunction>()&&obj->as<JSFunction>().isInterpreted())group->setInterpretedFunction(&obj->as<JSFunction>());obj->group_=group;returngroup;}/* static */boolJSObject::setNewGroupUnknown(JSContext*cx,constjs::Class*clasp,JS::HandleObjectobj){ObjectGroup::setDefaultNewGroupUnknown(cx,clasp,obj);returnJSObject::setFlags(cx,obj,BaseShape::NEW_GROUP_UNKNOWN);}/////////////////////////////////////////////////////////////////////// ObjectGroupCompartment NewTable//////////////////////////////////////////////////////////////////////* * Entries for the per-compartment set of groups which are the default * types to use for some prototype. An optional associated object is used which * allows multiple groups to be created with the same prototype. The * associated object may be a function (for types constructed with 'new') or a * type descriptor (for typed objects). These entries are also used for the set * of lazy groups in the compartment, which use a null associated object * (though there are only a few of these per compartment). */structObjectGroupCompartment::NewEntry{ReadBarrieredObjectGroupgroup;// Note: This pointer is only used for equality and does not need a read barrier.JSObject*associated;NewEntry(ObjectGroup*group,JSObject*associated):group(group),associated(associated){}structLookup{constClass*clasp;TaggedProtoproto;JSObject*associated;Lookup(constClass*clasp,TaggedProtoproto,JSObject*associated):clasp(clasp),proto(proto),associated(associated){}boolhasAssocId()const{return!associated||associated->zone()->hasUniqueId(associated);}boolensureAssocId()const{uint64_tunusedId;return!associated||associated->zoneFromAnyThread()->getUniqueId(associated,&unusedId);}uint64_tgetAssocId()const{returnassociated?associated->zone()->getUniqueIdInfallible(associated):0;}};staticboolhasHash(constLookup&l){returnl.proto.hasUniqueId()&&l.hasAssocId();}staticboolensureHash(constLookup&l){returnl.proto.ensureUniqueId()&&l.ensureAssocId();}staticinlineHashNumberhash(constLookup&lookup){MOZ_ASSERT(lookup.proto.hasUniqueId());MOZ_ASSERT(lookup.hasAssocId());HashNumberhash=uintptr_t(lookup.clasp);hash=mozilla::RotateLeft(hash,4)^Zone::UniqueIdToHash(lookup.proto.uniqueId());hash=mozilla::RotateLeft(hash,4)^Zone::UniqueIdToHash(lookup.getAssocId());returnhash;}staticinlineboolmatch(constObjectGroupCompartment::NewEntry&key,constLookup&lookup){TaggedProtoproto=key.group.unbarrieredGet()->proto();JSObject*assoc=key.associated;MOZ_ASSERT(proto.hasUniqueId());MOZ_ASSERT_IF(assoc,assoc->zone()->hasUniqueId(assoc));MOZ_ASSERT(lookup.proto.hasUniqueId());MOZ_ASSERT(lookup.hasAssocId());if(lookup.clasp&&key.group.unbarrieredGet()->clasp()!=lookup.clasp)returnfalse;if(proto.uniqueId()!=lookup.proto.uniqueId())returnfalse;return!assoc||assoc->zone()->getUniqueIdInfallible(assoc)==lookup.getAssocId();}staticvoidrekey(NewEntry&k,constNewEntry&newKey){k=newKey;}boolneedsSweep(){return(IsAboutToBeFinalized(&group)||(associated&&IsAboutToBeFinalizedUnbarriered(&associated)));}};namespacejs{template<>structFallibleHashMethods<ObjectGroupCompartment::NewEntry>{template<typenameLookup>staticboolhasHash(Lookup&&l){returnObjectGroupCompartment::NewEntry::hasHash(mozilla::Forward<Lookup>(l));}template<typenameLookup>staticboolensureHash(Lookup&&l){returnObjectGroupCompartment::NewEntry::ensureHash(mozilla::Forward<Lookup>(l));}};}// namespace jsclassObjectGroupCompartment::NewTable:publicJS::WeakCache<js::GCHashSet<NewEntry,NewEntry,SystemAllocPolicy>>{usingTable=js::GCHashSet<NewEntry,NewEntry,SystemAllocPolicy>;usingBase=JS::WeakCache<Table>;public:explicitNewTable(Zone*zone):Base(zone){}};MOZ_ALWAYS_INLINEObjectGroup*ObjectGroupCompartment::DefaultNewGroupCache::lookup(constClass*clasp,TaggedProtoproto,JSObject*associated){if(group_&&associated_==associated&&group_->proto()==proto&&(!clasp||group_->clasp()==clasp)){returngroup_;}returnnullptr;}/* static */ObjectGroup*ObjectGroup::defaultNewGroup(JSContext*cx,constClass*clasp,TaggedProtoproto,JSObject*associated){MOZ_ASSERT_IF(associated,proto.isObject());MOZ_ASSERT_IF(proto.isObject(),cx->isInsideCurrentCompartment(proto.toObject()));// A null lookup clasp is used for 'new' groups with an associated// function. The group starts out as a plain object but might mutate into an// unboxed plain object.MOZ_ASSERT_IF(!clasp,!!associated);if(associated&&!associated->is<TypeDescr>()){MOZ_ASSERT(!clasp);if(associated->is<JSFunction>()){// Canonicalize new functions to use the original one associated with its script.JSFunction*fun=&associated->as<JSFunction>();if(fun->hasScript())associated=fun->nonLazyScript()->functionNonDelazifying();elseif(fun->isInterpretedLazy()&&!fun->isSelfHostedBuiltin())associated=fun->lazyScript()->functionNonDelazifying();elseassociated=nullptr;// If we have previously cleared the 'new' script information for this// function, don't try to construct another one.if(associated&&associated->wasNewScriptCleared())associated=nullptr;}else{associated=nullptr;}if(!associated)clasp=&PlainObject::class_;}ObjectGroupCompartment&groups=cx->compartment()->objectGroups;if(ObjectGroup*group=groups.defaultNewGroupCache.lookup(clasp,proto,associated))returngroup;AutoEnterAnalysisenter(cx);ObjectGroupCompartment::NewTable*&table=groups.defaultNewTable;if(!table){table=cx->new_<ObjectGroupCompartment::NewTable>(cx->zone());if(!table||!table->init()){js_delete(table);table=nullptr;ReportOutOfMemory(cx);returnnullptr;}}if(proto.isObject()&&!proto.toObject()->isDelegate()){RootedObjectprotoObj(cx,proto.toObject());if(!JSObject::setDelegate(cx,protoObj))returnnullptr;// Objects which are prototypes of one another should be singletons, so// that their type information can be tracked more precisely. Limit// this group change to plain objects, to avoid issues with other types// of singletons like typed arrays.if(protoObj->is<PlainObject>()&&!protoObj->isSingleton()){if(!JSObject::changeToSingleton(cx,protoObj))returnnullptr;}}ObjectGroupCompartment::NewTable::AddPtrp=table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp,proto,associated));if(p){ObjectGroup*group=p->group;MOZ_ASSERT_IF(clasp,group->clasp()==clasp);MOZ_ASSERT_IF(!clasp,group->clasp()==&PlainObject::class_||group->clasp()==&UnboxedPlainObject::class_);MOZ_ASSERT(group->proto()==proto);groups.defaultNewGroupCache.put(group,associated);returngroup;}ObjectGroupFlagsinitialFlags=0;if(proto.isDynamic()||(proto.isObject()&&proto.toObject()->isNewGroupUnknown()))initialFlags=OBJECT_FLAG_DYNAMIC_MASK;Rooted<TaggedProto>protoRoot(cx,proto);ObjectGroup*group=ObjectGroupCompartment::makeGroup(cx,clasp?clasp:&PlainObject::class_,protoRoot,initialFlags);if(!group)returnnullptr;if(!table->add(p,ObjectGroupCompartment::NewEntry(group,associated))){ReportOutOfMemory(cx);returnnullptr;}if(associated){if(associated->is<JSFunction>()){if(!TypeNewScript::make(cx,group,&associated->as<JSFunction>()))returnnullptr;}else{group->setTypeDescr(&associated->as<TypeDescr>());}}/* * Some builtin objects have slotful native properties baked in at * creation via the Shape::{insert,get}initialShape mechanism. Since * these properties are never explicitly defined on new objects, update * the type information for them here. */constJSAtomState&names=cx->names();if(clasp==&RegExpObject::class_){AddTypePropertyId(cx,group,nullptr,NameToId(names.lastIndex),TypeSet::Int32Type());}elseif(clasp==&StringObject::class_){AddTypePropertyId(cx,group,nullptr,NameToId(names.length),TypeSet::Int32Type());}elseif(ErrorObject::isErrorClass(clasp)){AddTypePropertyId(cx,group,nullptr,NameToId(names.fileName),TypeSet::StringType());AddTypePropertyId(cx,group,nullptr,NameToId(names.lineNumber),TypeSet::Int32Type());AddTypePropertyId(cx,group,nullptr,NameToId(names.columnNumber),TypeSet::Int32Type());}groups.defaultNewGroupCache.put(group,associated);returngroup;}/* static */ObjectGroup*ObjectGroup::lazySingletonGroup(JSContext*cx,constClass*clasp,TaggedProtoproto){MOZ_ASSERT_IF(proto.isObject(),cx->compartment()==proto.toObject()->compartment());ObjectGroupCompartment::NewTable*&table=cx->compartment()->objectGroups.lazyTable;if(!table){table=cx->new_<ObjectGroupCompartment::NewTable>(cx->zone());if(!table||!table->init()){ReportOutOfMemory(cx);js_delete(table);table=nullptr;returnnullptr;}}ObjectGroupCompartment::NewTable::AddPtrp=table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp,proto,nullptr));if(p){ObjectGroup*group=p->group;MOZ_ASSERT(group->lazy());returngroup;}AutoEnterAnalysisenter(cx);Rooted<TaggedProto>protoRoot(cx,proto);ObjectGroup*group=ObjectGroupCompartment::makeGroup(cx,clasp,protoRoot,OBJECT_FLAG_SINGLETON|OBJECT_FLAG_LAZY_SINGLETON);if(!group)returnnullptr;if(!table->add(p,ObjectGroupCompartment::NewEntry(group,nullptr))){ReportOutOfMemory(cx);returnnullptr;}returngroup;}/* static */voidObjectGroup::setDefaultNewGroupUnknown(JSContext*cx,constClass*clasp,HandleObjectobj){// If the object already has a new group, mark that group as unknown.ObjectGroupCompartment::NewTable*table=cx->compartment()->objectGroups.defaultNewTable;if(table){Rooted<TaggedProto>taggedProto(cx,TaggedProto(obj));autolookup=ObjectGroupCompartment::NewEntry::Lookup(clasp,taggedProto,nullptr);autop=table->lookup(lookup);if(p)MarkObjectGroupUnknownProperties(cx,p->group);}}#ifdef DEBUG/* static */boolObjectGroup::hasDefaultNewGroup(JSObject*proto,constClass*clasp,ObjectGroup*group){ObjectGroupCompartment::NewTable*table=proto->compartment()->objectGroups.defaultNewTable;if(table){autolookup=ObjectGroupCompartment::NewEntry::Lookup(clasp,TaggedProto(proto),nullptr);autop=table->lookup(lookup);returnp&&p->group==group;}returnfalse;}#endif /* DEBUG */inlineconstClass*GetClassForProtoKey(JSProtoKeykey){switch(key){caseJSProto_Null:caseJSProto_Object:return&PlainObject::class_;caseJSProto_Array:return&ArrayObject::class_;caseJSProto_Number:return&NumberObject::class_;caseJSProto_Boolean:return&BooleanObject::class_;caseJSProto_String:return&StringObject::class_;caseJSProto_Symbol:return&SymbolObject::class_;caseJSProto_RegExp:return&RegExpObject::class_;caseJSProto_Int8Array:caseJSProto_Uint8Array:caseJSProto_Int16Array:caseJSProto_Uint16Array:caseJSProto_Int32Array:caseJSProto_Uint32Array:caseJSProto_Float32Array:caseJSProto_Float64Array:caseJSProto_Uint8ClampedArray:return&TypedArrayObject::classes[key-JSProto_Int8Array];caseJSProto_ArrayBuffer:return&ArrayBufferObject::class_;caseJSProto_SharedArrayBuffer:return&SharedArrayBufferObject::class_;caseJSProto_DataView:return&DataViewObject::class_;default:MOZ_CRASH("Bad proto key");}}/* static */ObjectGroup*ObjectGroup::defaultNewGroup(JSContext*cx,JSProtoKeykey){RootedObjectproto(cx);if(key!=JSProto_Null&&!GetBuiltinPrototype(cx,key,&proto))returnnullptr;returndefaultNewGroup(cx,GetClassForProtoKey(key),TaggedProto(proto.get()));}/////////////////////////////////////////////////////////////////////// ObjectGroupCompartment ArrayObjectTable/////////////////////////////////////////////////////////////////////structObjectGroupCompartment::ArrayObjectKey:publicDefaultHasher<ArrayObjectKey>{TypeSet::Typetype;ArrayObjectKey():type(TypeSet::UndefinedType()){}explicitArrayObjectKey(TypeSet::Typetype):type(type){}staticinlineuint32_thash(constArrayObjectKey&v){returnv.type.raw();}staticinlineboolmatch(constArrayObjectKey&v1,constArrayObjectKey&v2){returnv1.type==v2.type;}booloperator==(constArrayObjectKey&other){returntype==other.type;}booloperator!=(constArrayObjectKey&other){return!(*this==other);}boolneedsSweep(){MOZ_ASSERT(type.isUnknown()||!type.isSingleton());if(!type.isUnknown()&&type.isGroup()){ObjectGroup*group=type.groupNoBarrier();if(IsAboutToBeFinalizedUnbarriered(&group))returntrue;if(group!=type.groupNoBarrier())type=TypeSet::ObjectType(group);}returnfalse;}};staticinlineboolNumberTypes(TypeSet::Typea,TypeSet::Typeb){return(a.isPrimitive(JSVAL_TYPE_INT32)||a.isPrimitive(JSVAL_TYPE_DOUBLE))&&(b.isPrimitive(JSVAL_TYPE_INT32)||b.isPrimitive(JSVAL_TYPE_DOUBLE));}/* * As for GetValueType, but requires object types to be non-singletons with * their default prototype. These are the only values that should appear in * arrays and objects whose type can be fixed. */staticinlineTypeSet::TypeGetValueTypeForTable(constValue&v){TypeSet::Typetype=TypeSet::GetValueType(v);MOZ_ASSERT(!type.isSingleton());returntype;}/* static */JSObject*ObjectGroup::newArrayObject(JSContext*cx,constValue*vp,size_tlength,NewObjectKindnewKind,NewArrayKindarrayKind){MOZ_ASSERT(newKind!=SingletonObject);// If we are making a copy on write array, don't try to adjust the group as// getOrFixupCopyOnWriteObject will do this before any objects are copied// from this one.if(arrayKind==NewArrayKind::CopyOnWrite){ArrayObject*obj=NewDenseCopiedArray(cx,length,vp,nullptr,newKind);if(!obj||!ObjectElements::MakeElementsCopyOnWrite(cx,obj))returnnullptr;returnobj;}// Get a type which captures all the elements in the array to be created.Rooted<TypeSet::Type>elementType(cx,TypeSet::UnknownType());if(arrayKind!=NewArrayKind::UnknownIndex&&length!=0){elementType=GetValueTypeForTable(vp[0]);for(unsignedi=1;i<length;i++){TypeSet::Typentype=GetValueTypeForTable(vp[i]);if(ntype!=elementType){if(NumberTypes(elementType,ntype)){elementType=TypeSet::DoubleType();}else{elementType=TypeSet::UnknownType();break;}}}}ObjectGroupCompartment::ArrayObjectTable*&table=cx->compartment()->objectGroups.arrayObjectTable;if(!table){table=cx->new_<ObjectGroupCompartment::ArrayObjectTable>();if(!table||!table->init()){ReportOutOfMemory(cx);js_delete(table);table=nullptr;returnnullptr;}}ObjectGroupCompartment::ArrayObjectKeykey(elementType);DependentAddPtr<ObjectGroupCompartment::ArrayObjectTable>p(cx,*table,key);RootedObjectGroupgroup(cx);if(p){group=p->value();}else{RootedObjectproto(cx);if(!GetBuiltinPrototype(cx,JSProto_Array,&proto))returnnullptr;Rooted<TaggedProto>taggedProto(cx,TaggedProto(proto));group=ObjectGroupCompartment::makeGroup(cx,&ArrayObject::class_,taggedProto);if(!group)returnnullptr;AddTypePropertyId(cx,group,nullptr,JSID_VOID,elementType);if(elementType!=TypeSet::UnknownType()){// Keep track of the initial objects we create with this type.// If the initial ones have a consistent shape and property types, we// will try to use an unboxed layout for the group.PreliminaryObjectArrayWithTemplate*preliminaryObjects=cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);if(!preliminaryObjects)returnnullptr;group->setPreliminaryObjects(preliminaryObjects);}if(!p.add(cx,*table,ObjectGroupCompartment::ArrayObjectKey(elementType),group))returnnullptr;}// The type of the elements being added will already be reflected in type// information, but make sure when creating an unboxed array that the// common element type is suitable for the unboxed representation.ShouldUpdateTypesupdateTypes=ShouldUpdateTypes::DontUpdate;if(!MaybeAnalyzeBeforeCreatingLargeArray(cx,group,vp,length))returnnullptr;if(group->maybePreliminaryObjects())group->maybePreliminaryObjects()->maybeAnalyze(cx,group);if(group->maybeUnboxedLayout()){switch(group->unboxedLayout().elementType()){caseJSVAL_TYPE_BOOLEAN:if(elementType!=TypeSet::BooleanType())updateTypes=ShouldUpdateTypes::Update;break;caseJSVAL_TYPE_INT32:if(elementType!=TypeSet::Int32Type())updateTypes=ShouldUpdateTypes::Update;break;caseJSVAL_TYPE_DOUBLE:if(elementType!=TypeSet::Int32Type()&&elementType!=TypeSet::DoubleType())updateTypes=ShouldUpdateTypes::Update;break;caseJSVAL_TYPE_STRING:if(elementType!=TypeSet::StringType())updateTypes=ShouldUpdateTypes::Update;break;caseJSVAL_TYPE_OBJECT:if(elementType!=TypeSet::NullType()&&!elementType.get().isObjectUnchecked())updateTypes=ShouldUpdateTypes::Update;break;default:MOZ_CRASH();}}returnNewCopiedArrayTryUseGroup(cx,group,vp,length,newKind,updateTypes);}// Try to change the group of |source| to match that of |target|.staticboolGiveObjectGroup(JSContext*cx,JSObject*source,JSObject*target){MOZ_ASSERT(source->group()!=target->group());if(!target->is<ArrayObject>()&&!target->is<UnboxedArrayObject>())returntrue;if(target->group()->maybePreliminaryObjects()){boolforce=IsInsideNursery(source);target->group()->maybePreliminaryObjects()->maybeAnalyze(cx,target->group(),force);}if(target->is<ArrayObject>()){ObjectGroup*sourceGroup=source->group();if(source->is<UnboxedArrayObject>()){Shape*shape=target->as<ArrayObject>().lastProperty();if(!UnboxedArrayObject::convertToNativeWithGroup(cx,source,target->group(),shape))returnfalse;}elseif(source->is<ArrayObject>()){source->setGroup(target->group());}else{returntrue;}if(sourceGroup->maybePreliminaryObjects())sourceGroup->maybePreliminaryObjects()->unregisterObject(source);if(target->group()->maybePreliminaryObjects())target->group()->maybePreliminaryObjects()->registerNewObject(source);for(size_ti=0;i<source->as<ArrayObject>().getDenseInitializedLength();i++){Valuev=source->as<ArrayObject>().getDenseElement(i);AddTypePropertyId(cx,source->group(),source,JSID_VOID,v);}returntrue;}if(target->is<UnboxedArrayObject>()){if(!source->is<UnboxedArrayObject>())returntrue;if(source->as<UnboxedArrayObject>().elementType()!=JSVAL_TYPE_INT32)returntrue;if(target->as<UnboxedArrayObject>().elementType()!=JSVAL_TYPE_DOUBLE)returntrue;returnsource->as<UnboxedArrayObject>().convertInt32ToDouble(cx,target->group());}returntrue;}staticboolSameGroup(JSObject*first,JSObject*second){returnfirst->group()==second->group();}// When generating a multidimensional array of literals, such as// [[1,2],[3,4],[5.5,6.5]], try to ensure that each element of the array has// the same group. This is mainly important when the elements might have// different native vs. unboxed layouts, or different unboxed layouts, and// accessing the heterogenous layouts from JIT code will be much slower than// if they were homogenous.//// To do this, with each new array element we compare it with one of the// previous ones, and try to mutate the group of the new element to fit that// of the old element. If this isn't possible, the groups for all old elements// are mutated to fit that of the new element.booljs::CombineArrayElementTypes(JSContext*cx,JSObject*newObj,constValue*compare,size_tncompare){if(!ncompare||!compare[0].isObject())returntrue;JSObject*oldObj=&compare[0].toObject();if(SameGroup(oldObj,newObj))returntrue;if(!GiveObjectGroup(cx,newObj,oldObj))returnfalse;if(SameGroup(oldObj,newObj))returntrue;if(!GiveObjectGroup(cx,oldObj,newObj))returnfalse;if(SameGroup(oldObj,newObj)){for(size_ti=1;i<ncompare;i++){if(compare[i].isObject()&&!SameGroup(&compare[i].toObject(),newObj)){if(!GiveObjectGroup(cx,&compare[i].toObject(),newObj))returnfalse;}}}returntrue;}// Similarly to CombineArrayElementTypes, if we are generating an array of// plain objects with a consistent property layout, such as// [{p:[1,2]},{p:[3,4]},{p:[5.5,6.5]}], where those plain objects in// turn have arrays as their own properties, try to ensure that a consistent// group is given to each array held by the same property of the plain objects.booljs::CombinePlainObjectPropertyTypes(JSContext*cx,JSObject*newObj,constValue*compare,size_tncompare){if(!ncompare||!compare[0].isObject())returntrue;JSObject*oldObj=&compare[0].toObject();if(!SameGroup(oldObj,newObj))returntrue;if(newObj->is<PlainObject>()){if(newObj->as<PlainObject>().lastProperty()!=oldObj->as<PlainObject>().lastProperty())returntrue;for(size_tslot=0;slot<newObj->as<PlainObject>().slotSpan();slot++){ValuenewValue=newObj->as<PlainObject>().getSlot(slot);ValueoldValue=oldObj->as<PlainObject>().getSlot(slot);if(!newValue.isObject()||!oldValue.isObject())continue;JSObject*newInnerObj=&newValue.toObject();JSObject*oldInnerObj=&oldValue.toObject();if(SameGroup(oldInnerObj,newInnerObj))continue;if(!GiveObjectGroup(cx,newInnerObj,oldInnerObj))returnfalse;if(SameGroup(oldInnerObj,newInnerObj))continue;if(!GiveObjectGroup(cx,oldInnerObj,newInnerObj))returnfalse;if(SameGroup(oldInnerObj,newInnerObj)){for(size_ti=1;i<ncompare;i++){if(compare[i].isObject()&&SameGroup(&compare[i].toObject(),newObj)){ValueotherValue=compare[i].toObject().as<PlainObject>().getSlot(slot);if(otherValue.isObject()&&!SameGroup(&otherValue.toObject(),newInnerObj)){if(!GiveObjectGroup(cx,&otherValue.toObject(),newInnerObj))returnfalse;}}}}}}elseif(newObj->is<UnboxedPlainObject>()){constUnboxedLayout&layout=newObj->as<UnboxedPlainObject>().layout();constint32_t*traceList=layout.traceList();if(!traceList)returntrue;uint8_t*newData=newObj->as<UnboxedPlainObject>().data();uint8_t*oldData=oldObj->as<UnboxedPlainObject>().data();for(;*traceList!=-1;traceList++){}traceList++;for(;*traceList!=-1;traceList++){JSObject*newInnerObj=*reinterpret_cast<JSObject**>(newData+*traceList);JSObject*oldInnerObj=*reinterpret_cast<JSObject**>(oldData+*traceList);if(!newInnerObj||!oldInnerObj||SameGroup(oldInnerObj,newInnerObj))continue;if(!GiveObjectGroup(cx,newInnerObj,oldInnerObj))returnfalse;if(SameGroup(oldInnerObj,newInnerObj))continue;if(!GiveObjectGroup(cx,oldInnerObj,newInnerObj))returnfalse;if(SameGroup(oldInnerObj,newInnerObj)){for(size_ti=1;i<ncompare;i++){if(compare[i].isObject()&&SameGroup(&compare[i].toObject(),newObj)){uint8_t*otherData=compare[i].toObject().as<UnboxedPlainObject>().data();JSObject*otherInnerObj=*reinterpret_cast<JSObject**>(otherData+*traceList);if(otherInnerObj&&!SameGroup(otherInnerObj,newInnerObj)){if(!GiveObjectGroup(cx,otherInnerObj,newInnerObj))returnfalse;}}}}}}returntrue;}/////////////////////////////////////////////////////////////////////// ObjectGroupCompartment PlainObjectTable/////////////////////////////////////////////////////////////////////structObjectGroupCompartment::PlainObjectKey{jsid*properties;uint32_tnproperties;structLookup{IdValuePair*properties;uint32_tnproperties;Lookup(IdValuePair*properties,uint32_tnproperties):properties(properties),nproperties(nproperties){}};staticinlineHashNumberhash(constLookup&lookup){return(HashNumber)(HashId(lookup.properties[lookup.nproperties-1].id)^lookup.nproperties);}staticinlineboolmatch(constPlainObjectKey&v,constLookup&lookup){if(lookup.nproperties!=v.nproperties)returnfalse;for(size_ti=0;i<lookup.nproperties;i++){if(lookup.properties[i].id!=v.properties[i])returnfalse;}returntrue;}boolneedsSweep(){for(unsignedi=0;i<nproperties;i++){if(gc::IsAboutToBeFinalizedUnbarriered(&properties[i]))returntrue;}returnfalse;}};structObjectGroupCompartment::PlainObjectEntry{ReadBarrieredObjectGroupgroup;ReadBarrieredShapeshape;TypeSet::Type*types;boolneedsSweep(unsignednproperties){if(IsAboutToBeFinalized(&group))returntrue;if(IsAboutToBeFinalized(&shape))returntrue;for(unsignedi=0;i<nproperties;i++){MOZ_ASSERT(!types[i].isSingleton());if(types[i].isGroup()){ObjectGroup*group=types[i].groupNoBarrier();if(IsAboutToBeFinalizedUnbarriered(&group))returntrue;if(group!=types[i].groupNoBarrier())types[i]=TypeSet::ObjectType(group);}}returnfalse;}};staticboolCanShareObjectGroup(IdValuePair*properties,size_tnproperties){// Don't reuse groups for objects containing indexed properties, which// might end up as dense elements.for(size_ti=0;i<nproperties;i++){uint32_tindex;if(IdIsIndex(properties[i].id,&index))returnfalse;}returntrue;}staticboolAddPlainObjectProperties(JSContext*cx,HandlePlainObjectobj,IdValuePair*properties,size_tnproperties){RootedIdpropid(cx);RootedValuevalue(cx);for(size_ti=0;i<nproperties;i++){propid=properties[i].id;value=properties[i].value;if(!NativeDefineProperty(cx,obj,propid,value,nullptr,nullptr,JSPROP_ENUMERATE))returnfalse;}returntrue;}PlainObject*js::NewPlainObjectWithProperties(JSContext*cx,IdValuePair*properties,size_tnproperties,NewObjectKindnewKind){gc::AllocKindallocKind=gc::GetGCObjectKind(nproperties);RootedPlainObjectobj(cx,NewBuiltinClassInstance<PlainObject>(cx,allocKind,newKind));if(!obj||!AddPlainObjectProperties(cx,obj,properties,nproperties))returnnullptr;returnobj;}/* static */JSObject*ObjectGroup::newPlainObject(JSContext*cx,IdValuePair*properties,size_tnproperties,NewObjectKindnewKind){// Watch for simple cases where we don't try to reuse plain object groups.if(newKind==SingletonObject||nproperties==0||nproperties>=PropertyTree::MAX_HEIGHT)returnNewPlainObjectWithProperties(cx,properties,nproperties,newKind);ObjectGroupCompartment::PlainObjectTable*&table=cx->compartment()->objectGroups.plainObjectTable;if(!table){table=cx->new_<ObjectGroupCompartment::PlainObjectTable>();if(!table||!table->init()){ReportOutOfMemory(cx);js_delete(table);table=nullptr;returnnullptr;}}ObjectGroupCompartment::PlainObjectKey::Lookuplookup(properties,nproperties);ObjectGroupCompartment::PlainObjectTable::Ptrp=table->lookup(lookup);if(!p){if(!CanShareObjectGroup(properties,nproperties))returnNewPlainObjectWithProperties(cx,properties,nproperties,newKind);RootedObjectproto(cx);if(!GetBuiltinPrototype(cx,JSProto_Object,&proto))returnnullptr;Rooted<TaggedProto>tagged(cx,TaggedProto(proto));RootedObjectGroupgroup(cx,ObjectGroupCompartment::makeGroup(cx,&PlainObject::class_,tagged));if(!group)returnnullptr;gc::AllocKindallocKind=gc::GetGCObjectKind(nproperties);RootedPlainObjectobj(cx,NewObjectWithGroup<PlainObject>(cx,group,allocKind,TenuredObject));if(!obj||!AddPlainObjectProperties(cx,obj,properties,nproperties))returnnullptr;// Don't make entries with duplicate property names, which will show up// here as objects with fewer properties than we thought we were// adding to the object. In this case, reset the object's group to the// default (which will have unknown properties) so that the group we// just created will be collected by the GC.if(obj->slotSpan()!=nproperties){ObjectGroup*group=defaultNewGroup(cx,obj->getClass(),obj->taggedProto());if(!group)returnnullptr;obj->setGroup(group);returnobj;}// Keep track of the initial objects we create with this type.// If the initial ones have a consistent shape and property types, we// will try to use an unboxed layout for the group.PreliminaryObjectArrayWithTemplate*preliminaryObjects=cx->new_<PreliminaryObjectArrayWithTemplate>(obj->lastProperty());if(!preliminaryObjects)returnnullptr;group->setPreliminaryObjects(preliminaryObjects);preliminaryObjects->registerNewObject(obj);ScopedJSFreePtr<jsid>ids(group->zone()->pod_calloc<jsid>(nproperties));if(!ids){ReportOutOfMemory(cx);returnnullptr;}ScopedJSFreePtr<TypeSet::Type>types(group->zone()->pod_calloc<TypeSet::Type>(nproperties));if(!types){ReportOutOfMemory(cx);returnnullptr;}for(size_ti=0;i<nproperties;i++){ids[i]=properties[i].id;types[i]=GetValueTypeForTable(obj->getSlot(i));AddTypePropertyId(cx,group,nullptr,IdToTypeId(ids[i]),types[i]);}ObjectGroupCompartment::PlainObjectKeykey;key.properties=ids;key.nproperties=nproperties;MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key,lookup));ObjectGroupCompartment::PlainObjectEntryentry;entry.group.set(group);entry.shape.set(obj->lastProperty());entry.types=types;ObjectGroupCompartment::PlainObjectTable::AddPtrnp=table->lookupForAdd(lookup);if(!table->add(np,key,entry)){ReportOutOfMemory(cx);returnnullptr;}ids.forget();types.forget();returnobj;}RootedObjectGroupgroup(cx,p->value().group);// Watch for existing groups which now use an unboxed layout.if(group->maybeUnboxedLayout()){MOZ_ASSERT(group->unboxedLayout().properties().length()==nproperties);returnUnboxedPlainObject::createWithProperties(cx,group,newKind,properties);}// Update property types according to the properties we are about to add.// Do this before we do anything which can GC, which might move or remove// this table entry.if(!group->unknownProperties()){for(size_ti=0;i<nproperties;i++){TypeSet::Typetype=p->value().types[i];TypeSet::Typentype=GetValueTypeForTable(properties[i].value);if(ntype==type)continue;if(ntype.isPrimitive(JSVAL_TYPE_INT32)&&type.isPrimitive(JSVAL_TYPE_DOUBLE)){// The property types already reflect 'int32'.}else{if(ntype.isPrimitive(JSVAL_TYPE_DOUBLE)&&type.isPrimitive(JSVAL_TYPE_INT32)){// Include 'double' in the property types to avoid the update below later.p->value().types[i]=TypeSet::DoubleType();}AddTypePropertyId(cx,group,nullptr,IdToTypeId(properties[i].id),ntype);}}}RootedShapeshape(cx,p->value().shape);if(group->maybePreliminaryObjects())newKind=TenuredObject;gc::AllocKindallocKind=gc::GetGCObjectKind(nproperties);RootedPlainObjectobj(cx,NewObjectWithGroup<PlainObject>(cx,group,allocKind,newKind));if(!obj||!obj->setLastProperty(cx,shape))returnnullptr;for(size_ti=0;i<nproperties;i++)obj->setSlot(i,properties[i].value);if(group->maybePreliminaryObjects()){group->maybePreliminaryObjects()->registerNewObject(obj);group->maybePreliminaryObjects()->maybeAnalyze(cx,group);}returnobj;}/////////////////////////////////////////////////////////////////////// ObjectGroupCompartment AllocationSiteTable/////////////////////////////////////////////////////////////////////structObjectGroupCompartment::AllocationSiteKey:publicDefaultHasher<AllocationSiteKey>{ReadBarrieredScriptscript;uint32_toffset:24;JSProtoKeykind:8;ReadBarrieredObjectproto;staticconstuint32_tOFFSET_LIMIT=(1<<23);AllocationSiteKey(JSScript*script_,uint32_toffset_,JSProtoKeykind_,JSObject*proto_):script(script_),offset(offset_),kind(kind_),proto(proto_){MOZ_ASSERT(offset_<OFFSET_LIMIT);}AllocationSiteKey(constAllocationSiteKey&key):script(key.script),offset(key.offset),kind(key.kind),proto(key.proto){}AllocationSiteKey(AllocationSiteKey&&key):script(mozilla::Move(key.script)),offset(key.offset),kind(key.kind),proto(mozilla::Move(key.proto)){}voidoperator=(AllocationSiteKey&&key){script=mozilla::Move(key.script);offset=key.offset;kind=key.kind;proto=mozilla::Move(key.proto);}staticinlineuint32_thash(AllocationSiteKeykey){returnuint32_t(size_t(key.script.unbarrieredGet()->offsetToPC(key.offset))^key.kind^MovableCellHasher<JSObject*>::hash(key.proto.unbarrieredGet()));}staticinlineboolmatch(constAllocationSiteKey&a,constAllocationSiteKey&b){returnDefaultHasher<JSScript*>::match(a.script.unbarrieredGet(),b.script.unbarrieredGet())&&a.offset==b.offset&&a.kind==b.kind&&MovableCellHasher<JSObject*>::match(a.proto,b.proto);}voidtrace(JSTracer*trc){TraceRoot(trc,&script,"AllocationSiteKey script");TraceNullableRoot(trc,&proto,"AllocationSiteKey proto");}boolneedsSweep(){returnIsAboutToBeFinalizedUnbarriered(script.unsafeGet())||(proto&&IsAboutToBeFinalizedUnbarriered(proto.unsafeGet()));}};classObjectGroupCompartment::AllocationSiteTable:publicJS::WeakCache<js::GCHashMap<AllocationSiteKey,ReadBarrieredObjectGroup,AllocationSiteKey,SystemAllocPolicy>>{usingTable=js::GCHashMap<AllocationSiteKey,ReadBarrieredObjectGroup,AllocationSiteKey,SystemAllocPolicy>;usingBase=JS::WeakCache<Table>;public:explicitAllocationSiteTable(Zone*zone):Base(zone){}};/* static */ObjectGroup*ObjectGroup::allocationSiteGroup(JSContext*cx,JSScript*scriptArg,jsbytecode*pc,JSProtoKeykind,HandleObjectprotoArg/* = nullptr */){MOZ_ASSERT(!useSingletonForAllocationSite(scriptArg,pc,kind));MOZ_ASSERT_IF(protoArg,kind==JSProto_Array);uint32_toffset=scriptArg->pcToOffset(pc);if(offset>=ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT){if(protoArg)returndefaultNewGroup(cx,GetClassForProtoKey(kind),TaggedProto(protoArg));returndefaultNewGroup(cx,kind);}ObjectGroupCompartment::AllocationSiteTable*&table=cx->compartment()->objectGroups.allocationSiteTable;if(!table){table=cx->new_<ObjectGroupCompartment::AllocationSiteTable>(cx->zone());if(!table||!table->init()){ReportOutOfMemory(cx);js_delete(table);table=nullptr;returnnullptr;}}RootedScriptscript(cx,scriptArg);RootedObjectproto(cx,protoArg);if(!proto&&kind!=JSProto_Null&&!GetBuiltinPrototype(cx,kind,&proto))returnnullptr;Rooted<ObjectGroupCompartment::AllocationSiteKey>key(cx,ObjectGroupCompartment::AllocationSiteKey(script,offset,kind,proto));ObjectGroupCompartment::AllocationSiteTable::AddPtrp=table->lookupForAdd(key);if(p)returnp->value();AutoEnterAnalysisenter(cx);Rooted<TaggedProto>tagged(cx,TaggedProto(proto));ObjectGroup*res=ObjectGroupCompartment::makeGroup(cx,GetClassForProtoKey(kind),tagged,OBJECT_FLAG_FROM_ALLOCATION_SITE);if(!res)returnnullptr;if(JSOp(*pc)==JSOP_NEWOBJECT){// Keep track of the preliminary objects with this group, so we can try// to use an unboxed layout for the object once some are allocated.Shape*shape=script->getObject(pc)->as<PlainObject>().lastProperty();if(!shape->isEmptyShape()){PreliminaryObjectArrayWithTemplate*preliminaryObjects=cx->new_<PreliminaryObjectArrayWithTemplate>(shape);if(preliminaryObjects)res->setPreliminaryObjects(preliminaryObjects);elsecx->recoverFromOutOfMemory();}}if(kind==JSProto_Array&&(JSOp(*pc)==JSOP_NEWARRAY||IsCallPC(pc))&&cx->options().unboxedArrays()){PreliminaryObjectArrayWithTemplate*preliminaryObjects=cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);if(preliminaryObjects)res->setPreliminaryObjects(preliminaryObjects);elsecx->recoverFromOutOfMemory();}if(!table->add(p,key,res)){ReportOutOfMemory(cx);returnnullptr;}returnres;}voidObjectGroupCompartment::replaceAllocationSiteGroup(JSScript*script,jsbytecode*pc,JSProtoKeykind,ObjectGroup*group){AllocationSiteKeykey(script,script->pcToOffset(pc),kind,group->proto().toObjectOrNull());AllocationSiteTable::Ptrp=allocationSiteTable->lookup(key);MOZ_RELEASE_ASSERT(p);allocationSiteTable->get().remove(p);{AutoEnterOOMUnsafeRegionoomUnsafe;if(!allocationSiteTable->putNew(key,group))oomUnsafe.crash("Inconsistent object table");}}/* static */ObjectGroup*ObjectGroup::callingAllocationSiteGroup(JSContext*cx,JSProtoKeykey,HandleObjectproto){MOZ_ASSERT_IF(proto,key==JSProto_Array);jsbytecode*pc;RootedScriptscript(cx,cx->currentScript(&pc));if(script)returnallocationSiteGroup(cx,script,pc,key,proto);if(proto)returndefaultNewGroup(cx,GetClassForProtoKey(key),TaggedProto(proto));returndefaultNewGroup(cx,key);}/* static */boolObjectGroup::setAllocationSiteObjectGroup(JSContext*cx,HandleScriptscript,jsbytecode*pc,HandleObjectobj,boolsingleton){JSProtoKeykey=JSCLASS_CACHED_PROTO_KEY(obj->getClass());MOZ_ASSERT(key!=JSProto_Null);MOZ_ASSERT(singleton==useSingletonForAllocationSite(script,pc,key));if(singleton){MOZ_ASSERT(obj->isSingleton());/* * Inference does not account for types of run-once initializer * objects, as these may not be created until after the script * has been analyzed. */TypeScript::Monitor(cx,script,pc,ObjectValue(*obj));}else{ObjectGroup*group=allocationSiteGroup(cx,script,pc,key);if(!group)returnfalse;obj->setGroup(group);}returntrue;}/* static */ArrayObject*ObjectGroup::getOrFixupCopyOnWriteObject(JSContext*cx,HandleScriptscript,jsbytecode*pc){// Make sure that the template object for script/pc has a type indicating// that the object and its copies have copy on write elements.RootedArrayObjectobj(cx,&script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>());MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());if(obj->group()->fromAllocationSite()){MOZ_ASSERT(obj->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE));returnobj;}RootedObjectGroupgroup(cx,allocationSiteGroup(cx,script,pc,JSProto_Array));if(!group)returnnullptr;group->addFlags(OBJECT_FLAG_COPY_ON_WRITE);// Update type information in the initializer object group.MOZ_ASSERT(obj->slotSpan()==0);for(size_ti=0;i<obj->getDenseInitializedLength();i++){constValue&v=obj->getDenseElement(i);AddTypePropertyId(cx,group,nullptr,JSID_VOID,v);}obj->setGroup(group);returnobj;}/* static */ArrayObject*ObjectGroup::getCopyOnWriteObject(JSScript*script,jsbytecode*pc){// getOrFixupCopyOnWriteObject should already have been called for// script/pc, ensuring that the template object has a group with the// COPY_ON_WRITE flag. We don't assert this here, due to a corner case// where this property doesn't hold. See jsop_newarray_copyonwrite in// IonBuilder.ArrayObject*obj=&script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>();MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());returnobj;}/* static */boolObjectGroup::findAllocationSite(JSContext*cx,ObjectGroup*group,JSScript**script,uint32_t*offset){*script=nullptr;*offset=0;constObjectGroupCompartment::AllocationSiteTable*table=cx->compartment()->objectGroups.allocationSiteTable;if(!table)returnfalse;for(ObjectGroupCompartment::AllocationSiteTable::Ranger=table->all();!r.empty();r.popFront()){if(group==r.front().value()){*script=r.front().key().script;*offset=r.front().key().offset;returntrue;}}returnfalse;}/////////////////////////////////////////////////////////////////////// ObjectGroupCompartment/////////////////////////////////////////////////////////////////////ObjectGroupCompartment::ObjectGroupCompartment(){PodZero(this);}ObjectGroupCompartment::~ObjectGroupCompartment(){js_delete(defaultNewTable);js_delete(lazyTable);js_delete(arrayObjectTable);js_delete(plainObjectTable);js_delete(allocationSiteTable);stringSplitStringGroup=nullptr;}voidObjectGroupCompartment::removeDefaultNewGroup(constClass*clasp,TaggedProtoproto,JSObject*associated){autop=defaultNewTable->lookup(NewEntry::Lookup(clasp,proto,associated));MOZ_RELEASE_ASSERT(p);defaultNewTable->get().remove(p);defaultNewGroupCache.purge();}voidObjectGroupCompartment::replaceDefaultNewGroup(constClass*clasp,TaggedProtoproto,JSObject*associated,ObjectGroup*group){NewEntry::Lookuplookup(clasp,proto,associated);autop=defaultNewTable->lookup(lookup);MOZ_RELEASE_ASSERT(p);defaultNewTable->get().remove(p);defaultNewGroupCache.purge();{AutoEnterOOMUnsafeRegionoomUnsafe;if(!defaultNewTable->putNew(lookup,NewEntry(group,associated)))oomUnsafe.crash("Inconsistent object table");}}/* static */ObjectGroup*ObjectGroupCompartment::makeGroup(JSContext*cx,constClass*clasp,Handle<TaggedProto>proto,ObjectGroupFlagsinitialFlags/* = 0 */){MOZ_ASSERT_IF(proto.isObject(),cx->isInsideCurrentCompartment(proto.toObject()));ObjectGroup*group=Allocate<ObjectGroup>(cx);if(!group)returnnullptr;new(group)ObjectGroup(clasp,proto,cx->compartment(),initialFlags);returngroup;}/* static */ObjectGroup*ObjectGroupCompartment::getStringSplitStringGroup(JSContext*cx){ObjectGroupCompartment&groups=cx->compartment()->objectGroups;ObjectGroup*group=groups.stringSplitStringGroup.get();if(group){returngroup;}// The following code is a specialized version of the code// for ObjectGroup::allocationSiteGroup().constClass*clasp=GetClassForProtoKey(JSProto_Array);RootedObjectproto(cx);if(!GetBuiltinPrototype(cx,JSProto_Array,&proto))returnnullptr;Rooted<TaggedProto>tagged(cx,TaggedProto(proto));group=makeGroup(cx,clasp,tagged,/* initialFlags = */0);if(!group)returnnullptr;if(cx->options().unboxedArrays()){PreliminaryObjectArrayWithTemplate*preliminaryObjects=cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);if(preliminaryObjects)group->setPreliminaryObjects(preliminaryObjects);elsecx->recoverFromOutOfMemory();}groups.stringSplitStringGroup.set(group);returngroup;}voidObjectGroupCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOfmallocSizeOf,size_t*allocationSiteTables,size_t*arrayObjectGroupTables,size_t*plainObjectGroupTables,size_t*compartmentTables){if(allocationSiteTable)*allocationSiteTables+=allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);if(arrayObjectTable)*arrayObjectGroupTables+=arrayObjectTable->sizeOfIncludingThis(mallocSizeOf);if(plainObjectTable){*plainObjectGroupTables+=plainObjectTable->sizeOfIncludingThis(mallocSizeOf);for(PlainObjectTable::Enume(*plainObjectTable);!e.empty();e.popFront()){constPlainObjectKey&key=e.front().key();constPlainObjectEntry&value=e.front().value();/* key.ids and values.types have the same length. */*plainObjectGroupTables+=mallocSizeOf(key.properties)+mallocSizeOf(value.types);}}if(defaultNewTable)*compartmentTables+=defaultNewTable->sizeOfIncludingThis(mallocSizeOf);if(lazyTable)*compartmentTables+=lazyTable->sizeOfIncludingThis(mallocSizeOf);}voidObjectGroupCompartment::clearTables(){if(allocationSiteTable&&allocationSiteTable->initialized())allocationSiteTable->clear();if(arrayObjectTable&&arrayObjectTable->initialized())arrayObjectTable->clear();if(plainObjectTable&&plainObjectTable->initialized()){for(PlainObjectTable::Enume(*plainObjectTable);!e.empty();e.popFront()){constPlainObjectKey&key=e.front().key();PlainObjectEntry&entry=e.front().value();js_free(key.properties);js_free(entry.types);}plainObjectTable->clear();}if(defaultNewTable&&defaultNewTable->initialized())defaultNewTable->clear();if(lazyTable&&lazyTable->initialized())lazyTable->clear();defaultNewGroupCache.purge();}/* static */boolObjectGroupCompartment::PlainObjectTableSweepPolicy::needsSweep(PlainObjectKey*key,PlainObjectEntry*entry){if(!(JS::GCPolicy<PlainObjectKey>::needsSweep(key)||entry->needsSweep(key->nproperties)))returnfalse;js_free(key->properties);js_free(entry->types);returntrue;}voidObjectGroupCompartment::sweep(FreeOp*fop){/* * Iterate through the array/object group tables and remove all entries * referencing collected data. These tables only hold weak references. */if(arrayObjectTable)arrayObjectTable->sweep();if(plainObjectTable)plainObjectTable->sweep();if(stringSplitStringGroup){if(JS::GCPolicy<ReadBarrieredObjectGroup>::needsSweep(&stringSplitStringGroup)){stringSplitStringGroup=nullptr;}}}voidObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable*table){/* * Each entry's hash depends on the object's prototype and we can't tell * whether that has been moved or not in sweepNewObjectGroupTable(). */if(table&&table->initialized()){for(NewTable::Enume(*table);!e.empty();e.popFront()){NewEntry&entry=e.mutableFront();ObjectGroup*group=entry.group.unbarrieredGet();if(IsForwarded(group)){group=Forwarded(group);entry.group.set(group);}TaggedProtoproto=group->proto();if(proto.isObject()&&IsForwarded(proto.toObject())){proto=TaggedProto(Forwarded(proto.toObject()));// Update the group's proto here so that we are able to lookup// entries in this table before all object pointers are updated.group->proto()=proto;}if(entry.associated&&IsForwarded(entry.associated))entry.associated=Forwarded(entry.associated);}}}#ifdef JSGC_HASH_TABLE_CHECKSvoidObjectGroupCompartment::checkNewTableAfterMovingGC(NewTable*table){/* * Assert that nothing points into the nursery or needs to be relocated, and * that the hash table entries are discoverable. */if(!table||!table->initialized())return;for(autor=table->all();!r.empty();r.popFront()){NewEntryentry=r.front();CheckGCThingAfterMovingGC(entry.group.unbarrieredGet());TaggedProtoproto=entry.group.unbarrieredGet()->proto();if(proto.isObject())CheckGCThingAfterMovingGC(proto.toObject());CheckGCThingAfterMovingGC(entry.associated);constClass*clasp=entry.group.unbarrieredGet()->clasp();if(entry.associated&&entry.associated->is<JSFunction>())clasp=nullptr;NewEntry::Lookuplookup(clasp,proto,entry.associated);autoptr=table->lookup(lookup);MOZ_RELEASE_ASSERT(ptr.found()&&&*ptr==&r.front());}}#endif // JSGC_HASH_TABLE_CHECKS